package statistics.poznavaniTextu;


import Jama.EigenvalueDecomposition;
import Jama.Matrix;
import com.sun.corba.se.spi.orbutil.fsm.Input;
import com.sun.org.apache.bcel.internal.generic.RET;
import org.omg.CosNaming._NamingContextExtStub;
import stuff.FileManager;
import sun.org.mozilla.javascript.internal.ast.ForInLoop;


import javax.swing.*;
import java.awt.geom.Point2D;
import java.io.*;
import java.sql.Array;
import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;


/*
*   File MnozinaCharakteristik
*   in package statistics.poznavaniTextu
*   was created by slaha
*   on 14.4.12 at 21:21.
*
*/
public class MnozinaCharakteristik extends ACharakteristiky implements Runnable {
  ArrayList<CharakteristikaTextu> zdrojoveHodnoty = new ArrayList<CharakteristikaTextu>();
  double[] hodnotyZ1 = null;
  double[] hodnotyZ2 = null;
  HashMap<String, ArrayList<PointBasen>> autoriBody = new HashMap<>();
  double sumaVsechLambda, sumaDvouLambda;
  //ArrayList<CharakteristikaTextu> hodnoty = new ArrayList<CharakteristikaTextu>();


  public MnozinaCharakteristik() {
    MnozinaCharakteristik temp = load();
    if (temp != null) {
      this.zdrojoveHodnoty = temp.zdrojoveHodnoty;
      this.hodnotyZ1 = temp.hodnotyZ1;
      this.hodnotyZ2 = temp.hodnotyZ2;
      this.sumaVsechLambda = temp.sumaVsechLambda;
      this.sumaDvouLambda = temp.sumaDvouLambda;
    }
  }


  public void add(String jmeno, String text, String autor) {
    ArrayList<String> al = new ArrayList<>();
    al.add(text);
    zdrojoveHodnoty.add(new CharakteristikaTextu(jmeno, al, autor));
    save(this);
  }


  public void add(HashMap<String, String> hm, String autor) {
    for (String jmeno : hm.keySet()) {
      add(jmeno, hm.get(jmeno), autor);
    }
  }


  @Override
  public void run() {

    //..průměry a odchylky jevů
    HashMap<String, Double> charakteristiky = dejCharakteristiky();

    //..pro všechny zdrojové hodnoty


    //..kovarianční matice
    int N = zdrojoveHodnoty.get(0).hodnoty.size();
    double[][] kovariancniMatice = new double[N][N];


    //..vezmeme jevy, co sledujem
    String[] jevy = new String[N];
    jevy = zdrojoveHodnoty.get(0).hodnoty.keySet().toArray(jevy);


    //..výpočet kovarianční matice
    for (int i = 0; i < N; i++) {
      for (int j = i; j < N; j++) {
        if (i == j) {
          //..rozptyl
          kovariancniMatice[i][j] = rozptyl(jevy[i], charakteristiky.get(jevy[i] + prumerStr));
        } else {
          //..kovariance
          double kovariance = kovariance(jevy[i], jevy[j], charakteristiky);
          kovariancniMatice[i][j] = kovariance;
          kovariancniMatice[j][i] = kovariance;
        }
      }
    }


    Jama.Matrix matrix = new Matrix(kovariancniMatice);


    Jama.EigenvalueDecomposition vlHodnoty = new EigenvalueDecomposition(matrix);
    double[] lambdy = vlHodnoty.getRealEigenvalues();
    Jama.Matrix vlVektory = vlHodnoty.getV();

    this.sumaVsechLambda = nastavSoucetVsech(lambdy);
    int[] indexNejvyssi = dejIndexyNejvyssich(lambdy);
    this.sumaDvouLambda = lambdy[indexNejvyssi[0]] + lambdy[indexNejvyssi[1]];


    this.hodnotyZ1 = vlVektory.getMatrix(0, vlVektory.getRowDimension()-1, indexNejvyssi[0], indexNejvyssi[0]).getRowPackedCopy();
    this.hodnotyZ2 = vlVektory.getMatrix(0, vlVektory.getRowDimension()-1, indexNejvyssi[1], indexNejvyssi[1]).getRowPackedCopy();

    normuj();

    save(this);

  }


  /**
   * Vrátí průměr a odchylku všech jevů
   *
   * @return
   */
  private HashMap<String, Double> dejCharakteristiky() {
    HashMap<String, Double> hm = new HashMap<>();


    //..pro všechny zdrojové hodnoty
    for (CharakteristikaTextu chT : zdrojoveHodnoty) {
      //.. každý jev
      for (String jev : chT.hodnoty.keySet()) {
        double prum = prumer(jev);
        hm.put(jev + prumerStr, prum);
        hm.put(jev + odchylkaStr, odchylka(jev, prum));
      }
    }
    return hm;
  }


  private double prumer(String jev) {
    double prumer = 0d;
    double pocetNaN = 0;


    for (CharakteristikaTextu chT : zdrojoveHodnoty) {
      if (chT.hodnoty.get(jev) == Double.NaN || chT.hodnoty.get(jev) == Double.MIN_VALUE) {
        pocetNaN++;
      } else {
        prumer += chT.hodnoty.get(jev);
      }
    }


    return prumer / (double) (zdrojoveHodnoty.size() - pocetNaN);
  }


  private double rozptyl(String jev, double prumerJevu) {
    double rozptyl = 0d;
    double n_1 = (double) zdrojoveHodnoty.size() - 1d;


    for (CharakteristikaTextu chT : zdrojoveHodnoty) {
      double clen = chT.hodnoty.get(jev) - prumerJevu;
      clen = Math.pow(clen, 2);
      rozptyl += clen;
    }


    return (rozptyl / n_1);
  }


  private double kovariance(String jevX, String jevY, HashMap<String, Double> hm) {
    double kovariance = 0d;
    double n_1 = (double) zdrojoveHodnoty.size() - 1d;
    double prumerX = hm.get(jevX + prumerStr);
    double prumerY = hm.get(jevY + prumerStr);


    for (CharakteristikaTextu chT : zdrojoveHodnoty) {
      double clen1 = chT.hodnoty.get(jevX) - prumerX;
      double clen2 = chT.hodnoty.get(jevY) - prumerY;
      kovariance += (clen1 * clen2);
    }


    return (kovariance / n_1);
  }


  private double odchylka(String jev, double prumer) {
    double odchylka = 1d / (double) zdrojoveHodnoty.size();


    double soucet = 0d;
    for (CharakteristikaTextu chT : zdrojoveHodnoty) {
      double rozdil = chT.hodnoty.get(jev) - prumer;
      rozdil = Math.pow(rozdil, 2);
      soucet += rozdil;
    }


    odchylka *= soucet;


    return Math.sqrt(odchylka);
  }


  public void save(ACharakteristiky aCharakteristiky) {
    String jmeno = FileManager.getFolder() + "/" + jmenoSouboru;


    try {
      //use buffering
      OutputStream file = new FileOutputStream(jmeno);
      OutputStream buffer = new BufferedOutputStream(file);
      ObjectOutput output = new ObjectOutputStream(buffer);
      try {
        output.writeObject(aCharakteristiky);
      } finally {
        output.flush();
        output.close();
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }


  int[] dejIndexyNejvyssich(double[] lambdy) {
    int[] indexy = new int[2];
    indexy[0] = (Math.abs(lambdy[0]) >= Math.abs(lambdy[1])) ? 0 : 1;
    indexy[1] = (Math.abs(lambdy[0]) >= Math.abs(lambdy[1])) ? 1 : 0;
    double max1 = indexy[0];
    double max2 = indexy[1];


    for (int i = 2; i < lambdy.length; i++) {
      if (Math.abs(lambdy[i]) > max1) {
        //..je nejvyšší
        indexy[1] = indexy[0]; //..dosavadní nejvyšší je 2. nejvyšší
        indexy[0] = i; //.. nejvyšší je aktualní
        max1 = indexy[0];
        max2 = indexy[1];
      } else if (Math.abs(lambdy[i]) > max2 && Math.abs(lambdy[i]) < max1) {
        //..je druhý nejvyšší
        indexy[1] = i; //.. nejvyšší je aktualní
        max2 = indexy[1];
      }
    }

    return indexy;
  }

  double nastavSoucetVsech(double[] lambdy) {
    double suma = 0d;
    for (double lambda : lambdy) {
      suma += lambda;
    }

    return suma;
  }


  public MnozinaCharakteristik load() {
    MnozinaCharakteristik a = null;
    String jmeno = FileManager.getFolder() + "/" + jmenoSouboru;
    File soubor = new File(jmeno);
    if (!soubor.exists()) {
      return null;
    }


    try {
      //use buffering
      InputStream file = new FileInputStream(soubor);
      InputStream buffer = new BufferedInputStream(file);
      ObjectInput input = new ObjectInputStream(buffer);
      try {
        //deserialize
        a = (MnozinaCharakteristik) input.readObject();
      } finally {
        input.close();
      }
    } catch (ClassNotFoundException ex) {
      ex.printStackTrace();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return a;
  }


  public void kresli() {
    int pocetCharakteristik = hodnotyZ1.length;


    //.. každá báseň
    for (CharakteristikaTextu chT : zdrojoveHodnoty) {
      double X = 0;
      double Y = 0;
      Double[] hodnotyPole = new Double[pocetCharakteristik];
      hodnotyPole = chT.hodnoty.values().toArray(hodnotyPole);
      for (int i = 0; i < pocetCharakteristik; i++) {
        X += hodnotyZ1[i] * hodnotyPole[i];
        Y += (-hodnotyZ2[i]) * hodnotyPole[i];
      }
      PointBasen pB = new PointBasen((X), (Y), chT.jmeno);
      System.out.println(X);
      System.out.println(Y);
      System.out.println("------------");
      String autor = chT.autor;


      if (autoriBody.containsKey(autor)) {
        autoriBody.get(autor).add(pB);
      } else {
        ArrayList<PointBasen> al = new ArrayList<>();
        al.add(pB);
        autoriBody.put(autor, al);
      }
    }
  }

  public HashMap<String, ArrayList<PointBasen>> getAutoriBody() {
    if (hodnotyZ1 == null || hodnotyZ2 == null) {
      run();
    }
    kresli();
    return autoriBody;
  }


  void normuj() {
    double sumaZ1 = 0d;
    double sumaZ2 = 0d;
    for (int i = 0; i < hodnotyZ1.length; i++) {
      sumaZ1 += Math.pow(hodnotyZ1[i], 2);
      sumaZ2 += Math.pow(hodnotyZ2[i], 2);
    }

    for (int i = 0; i < hodnotyZ1.length; i++) {
      this.hodnotyZ1[i] = hodnotyZ1[i] / sumaZ1;
      this.hodnotyZ2[i] = hodnotyZ2[i] / sumaZ2;
    }
  }

  public double getSumaVsechLambda() {
    return sumaVsechLambda;
  }

  public double getSumaDvouLambda() {
    return sumaDvouLambda;
  }

  public static void main(String[] args) {
    double[][] m = new double[3][3];
    m[0][0] = 1;
    m[0][1] = 2;
    m[0][2] = 3;
    m[1][0] = 3;
    m[1][1] = 5;
    m[1][2] = 7;
    m[2][0] = -2;
    m[2][1] = 1;
    m[2][2] = 3;


    Matrix mat = new Matrix(m);
    
    Matrix v = mat.eig().getV();
  }
}
